// HID input and a HID message Hid hi; HidMsg msg; // which mouse 2 => int device; // get from command line if (me.args()) me.arg(0) => Std.atoi => device; // open mouse 0, exit on fail if (!hi.openMouse(device)) me.exit(); <<< "mouse '" + hi.name() + "' ready", "" >>>; //GG.fullscreen(); GGen sceneGen --> GG.scene(); //sceneGen.sca(.5); GCamera camera; GG.scene().backgroundColor(@(.85,.8,1)); camera.posY(2); camera.rot(@(-.025*pi,0,0)); GPlane ground --> sceneGen; FlatMaterial groundMat => ground.mat; ground.mat().color(@(.5,.7,.4)); ground.mat().shine(.1); ground.posY(-1.5); ground.posZ(-10); ground.rot(@(pi/2,0,0)); ground.sca(10000); 0 => int GROUND_MODE; 1 => int FLY_MODE; GROUND_MODE => int mode; //Mountain class GMountain extends GMesh { 20 => float size; //4 => float maxHeight; 20 => int NPTS; float heights[NPTS][NPTS]; vec3 positions[NPTS * NPTS]; Math.random() => int srand; Math.srandom(srand); <<<"random seed", srand>>>; for (1 => int i; i < NPTS - 1; i++) { for (1 => int j; j < NPTS - 1; j++) { // TODO: dep on i, j? // TODO: noise and then lowpass? Math.random2f(0,1.2) => heights[i][j]; //heights[i][j] + 1 * Math.random2f(-.5,1) => heights[i][j]; } } for (0 => int i; i < NPTS ; i++) { for (0 => int j; j < NPTS; j++) { //@(i-(NPTS-1)/2.,j-(NPTS-1)/2.,heights[i][j]) => positions[i * NPTS + j]; size * @(i-(NPTS-1)/2.,heights[i][j],-j) => positions[i * NPTS + j]; } } (NPTS - 1) => int numSides; int indices[3 * 2 * numSides * numSides]; for (0 => int i; i < numSides; i++) { for (0 => int j; j < numSides; j++) { (i * numSides + j) * 6 => int offset; i * NPTS + j => indices[offset]; i * NPTS + j + 1 => indices[offset + 1]; (i + 1) * NPTS + j => indices[offset + 2]; (i + 1) * NPTS + j + 1 => indices[offset + 3]; i * NPTS + j + 1 => indices[offset + 4]; (i + 1) * NPTS + j => indices[offset + 5]; } } fun vec3 cross(vec3 a, vec3 b) { return @(-a.z*b.y+a.y*b.z, a.z*b.x-a.x*b.z, -a.y*b.x+a.x*b.y); } fun float calcNorm(vec3 in) { return Math.sqrt(Math.pow(in.x,2) + Math.pow(in.y,2) + Math.pow(in.z,2)); } fun vec3 normalize(vec3 in) { return in / calcNorm(in); } vec3 surfNormals[numSides][numSides][2]; for (0 => int i; i < numSides; i++) { for (0 => int j; j < numSides; j++) { for (0 => int k; k < 2; k++) { (i * numSides + j) * 6 + k * 3 => int triOffset; positions[indices[triOffset]] => vec3 a; positions[indices[triOffset + 1]] => vec3 b; positions[indices[triOffset + 2]] => vec3 c; normalize(cross(a - b, c - b)) => surfNormals[i][j][k]; } } } vec3 vertNormals[NPTS * NPTS]; // TODO: corners for (1 => int i; i < NPTS - 1 ; i++) { // TODO: sides for (1 => int j; j < NPTS - 1; j++) { i * NPTS + j => int index; vec3 sum; for (0 => int k; k < 2; k++) { for (0 => int l; l < 2; l++) { //<<>>; surfNormals[i-1+k][j-1+l][0] => sum; surfNormals[i-1+k][j-1+l][1] +=> sum; } } normalize(sum) => vertNormals[index]; } } CustomGeometry mtnGeo; mtnGeo.positions(positions); mtnGeo.indices(indices); mtnGeo.normals(vertNormals); PhongMaterial myMat; myMat.color(Color.BROWN); this.set(mtnGeo,myMat); } GMountain mtn --> sceneGen; mtn.pos(ground.posWorld()); mtn.translate(@(0,0,-40)); // Swing GGen swing --> sceneGen; -3 => float swingPosX; // -3 swing.pos(@(swingPosX,0,-13)); @(0,0,0) => vec3 barColor; Math.pi*.9 => float swingRot; // Math.pi*.9 swing.rot(@(0,swingRot,0)); 1.2 => float swingSca; GCylinder topBar --> swing; topBar.mat().color(barColor); topBar.rot(@(0,0,Math.pi/2)); topBar.sca(swingSca*@(1,10,1)); topBar.posY(4); GGen leftHinge --> swing; leftHinge.posX(-topBar.scaY()/2); leftHinge.posY(topBar.posY()); GSphere leftHingeBall --> leftHinge; leftHingeBall.sca(swingSca*.4); leftHingeBall.mat().color(barColor); GGen leftHingeRot1 --> leftHinge; GCylinder leftLeg1 --> leftHingeRot1; leftLeg1.mat().color(barColor); leftLeg1.sca(swingSca*@(1,6,1)); leftLeg1.posY(-leftLeg1.scaY()/2); leftHingeRot1.rot(@(-pi/6,0,0)); GGen leftHingeRot2 --> leftHinge; GCylinder leftLeg2 --> leftHingeRot2; leftLeg2.mat().color(barColor); leftLeg2.sca(swingSca*@(1,6,1)); leftLeg2.posY(-leftLeg2.scaY()/2); leftHingeRot2.rot(@(pi/6,0,0)); GGen rightHinge --> swing; rightHinge.posX(topBar.scaY()/2); rightHinge.posY(topBar.posY()); GSphere rightHingeBall --> rightHinge; rightHingeBall.sca(swingSca*.4); rightHingeBall.mat().color(barColor); GGen rightHingeRot1 --> rightHinge; GCylinder rightLeg1 --> rightHingeRot1; rightLeg1.mat().color(barColor); rightLeg1.sca(swingSca*@(1,6,1)); rightLeg1.posY(-rightLeg1.scaY()/2); rightHingeRot1.rot(@(-pi/6,0,0)); GGen rightHingeRot2 --> rightHinge; GCylinder rightLeg2 --> rightHingeRot2; rightLeg2.mat().color(barColor); rightLeg2.sca(swingSca*@(1,6,1)); rightLeg2.posY(-rightLeg2.scaY()/2); rightHingeRot2.rot(@(pi/6,0,0)); GGen swingArm --> swing; swingArm.posY(topBar.posY()); 10 => int nSeatPPS; // Points per side 2 * nSeatPPS + 1 => int nSeatPoints; // * 2 for triangles GMesh seatMeshes[(nSeatPoints - 1) * 2]; CustomGeometry seatGeos[(nSeatPoints - 1) * 2]; PhongMaterial seatMat; seatMat.color(barColor); GGen seat --> swingArm; seat.rot(@(pi/2,0,0)); seat.posY(-.3 - topBar.posY()); //seat.posY(-3.1); 2.5 => float seatWidth; .8 => float seatLength; 3.5 => float seatCurveAmp; 2 => float seatCurvePow; seatWidth/nSeatPoints => float seatMeshWidth; for (0 => int p; p < nSeatPoints - 1; 1 +=> p) { // one triangle seatMeshes[2 * p] --> seat; seatMeshes[2 * p].posX((p + .5 - nSeatPPS) * seatWidth / nSeatPoints); seatMeshes[2 * p].set(seatGeos[2 * p], seatMat); // other triangle seatMeshes[2 * p + 1] --> seat; seatMeshes[2 * p + 1].posX((p + .5 - nSeatPPS) * seatWidth / nSeatPoints); seatMeshes[2 * p + 1].set(seatGeos[2 * p + 1], seatMat); Math.abs(nSeatPPS - p)$float / nSeatPoints => float lDistance; Math.abs(nSeatPPS - 1 - p)$float / nSeatPoints => float rDistance; -seatCurveAmp * Math.pow(lDistance,seatCurvePow) => float lHeight; -seatCurveAmp * Math.pow(rDistance,seatCurvePow) => float rHeight; // pointing down triangle seatGeos[2 * p].positions([@(-seatMeshWidth/2,seatLength/2,lHeight/2), @(seatMeshWidth/2,seatLength/2,rHeight/2), @(-seatMeshWidth/2,-seatLength/2,lHeight/2)]); // pointing up triangle seatGeos[2 * p + 1].positions([@(-seatMeshWidth/2,-seatLength/2,lHeight/2), @(seatMeshWidth/2,-seatLength/2,rHeight/2), @(seatMeshWidth/2,seatLength/2,rHeight/2)]); } seatWidth / 2 => float chainX; .1 => float chainPosY; [@(0,topBar.posY(),0),@(0,0,0)] @=> vec3 chainPositions[]; GLines lChain --> swingArm; lChain.geo().positions(chainPositions); lChain.mat().color(barColor); lChain.posX(-chainX); lChain.posY(chainPosY - topBar.posY()); GLines rChain --> swingArm; rChain.geo().positions(chainPositions); rChain.mat().color(barColor); rChain.posX(chainX); rChain.posY(chainPosY - topBar.posY()); // You GGen you --> swingArm; @(.7,.8,1) => vec3 youColor; you.pos(@(0,.7 - topBar.posY(),0)); you.posWorld() => vec3 initYouPosWorld; GSphere torso --> you; torso.mat().color(youColor); torso.scaY(1.8); torso.scaX(.97); torso.posY(.022); GSphere head --> you; head.mat().color(youColor); head.posY(1.55); head.sca(.72); GGen legs --> you; @(.46,1,.46) => vec3 thighSca; .26 => float legX; -.78 => float thighY; legs.posY(-1); GSphere rThigh --> legs; rThigh.mat().color(youColor); rThigh.posY(thighY); rThigh.posX(legX); rThigh.sca(thighSca); GSphere lThigh --> legs; lThigh.mat().color(youColor); lThigh.posY(thighY); lThigh.posX(-legX); lThigh.sca(thighSca); legs.rotX(-Math.pi * .5); GGen knees --> legs; @(.42,.94,.42) => vec3 calfSca; knees.posY(-thighSca.y - .47); -.7 => float calfY; GSphere rCalf --> knees; rCalf.mat().color(youColor); rCalf.posY(calfY); rCalf.posX(legX); rCalf.sca(calfSca); GSphere lCalf --> knees; lCalf.mat().color(youColor); lCalf.posY(calfY); lCalf.posX(-legX); lCalf.sca(calfSca); knees.rotX(Math.pi * .5); // TODO: turn arms up? @(.39,1.4,.39) => vec3 armSca; .9 => float shoulderY; .55 => float shoulderX; 1./6 * Math.pi => float shoulderRot; -1 => float armPosY; GSphere rArm --> GGen rShoulder --> you; rArm.mat().color(youColor); rArm.sca(armSca); rArm.posY(armPosY); rShoulder.posY(shoulderY); rShoulder.posX(shoulderX); rShoulder.rotZ(shoulderRot); GSphere lArm --> GGen lShoulder --> you; lArm.mat().color(youColor); lArm.sca(armSca); lArm.posY(armPosY); lShoulder.posY(shoulderY); lShoulder.posX(-shoulderX); lShoulder.rotZ(-shoulderRot); Step step => ADSR kneeAngleEnv => UGen kneeAngle => blackhole; step.next(1); //kneeAngleEnv.duration(.15::second); kneeAngleEnv.set(.15,0,1,.26); //step => ADSR shoulderAngleEnv => UGen shoulderAngle => blackhole; //shoulderAngleEnv.set(.15,0,1,.26); // Mouses false => int mouseDown; fun void onMouseDown() { if (mode == GROUND_MODE) kneeAngleEnv.keyOn(); //else if (mode == FLY_MODE) // shoulderAngleEnv.keyOn(); true => mouseDown; } fun void onMouseUp() { if (mode == GROUND_MODE) kneeAngleEnv.keyOff(); //else if (mode == FLY_MODE) // shoulderAngleEnv.keyOff(); false => mouseDown; } fun void trackMouse() { while (true) { hi => now; while (hi.recv(msg)) { // TODO: try wheel motion instead? msg.isWheelMotion(), msg.deltaX,Y if (msg.isButtonDown()) { onMouseDown(); } else if (msg.isButtonUp()) { onMouseUp(); } } } } spork ~ trackMouse(); // Audio // TODO: add notes as it goes on. 10::ms => dur physT; .5 => float totalGain; 1./4 => float speedG; class Song { 3 => int numOscs; numOscs => int nOn; TriOsc oscs[numOscs]; Envelope envs[numOscs]; [16., 7, 0] @=> float forwardPitches[]; [17., 8, 2] @=> float backPitches[]; 60 + 12 => float rootPitch; Gain g => LPF filt => dac; g.gain(0); filt.freq(1600); for (0 => int i; i < numOscs; i++) { oscs[i] => envs[i] => g; envs[i].duration(6::second); } fun void setPitches(int forward) { forward ? forwardPitches : backPitches @=> float pitches[]; for (0 => int i; i < numOscs; i++) { rootPitch + pitches[i] => Std.mtof => oscs[i].freq; } for (0 => int i; i < nOn; i++) { envs[i].keyOn(); } for (nOn => int i; i < numOscs; i++) { envs[i].keyOff(); } } fun void setG(float speed) { setPitches(speed >= 0); //g.gain() + .01 * (Math.fabs(swingArmAngle) / pi * totalGain - g.gain()) => g.gain; //Math.pow(Math.fabs(swingArmAngle) / pi, 2) + Math.pow(Math.fabs(omega * GG.fps()) / pi, 2) * totalGain => g.gain; Math.pow(Math.fabs(speed / (physT/second)) * speedG, 2) * totalGain => g.gain; //<<>>; } fun void setNPitches(int n) { n => nOn; } } Song youSong; youSong.setNPitches(1); // TODO: open up filter with envelope. // TODO: butterflies? // TODO: add harmonies one flyng starts? // Dynamics 0*4*pi/5/2 => float swingArmAngle; // 4*pi/5/2, 0 .02 => float gAccel; gAccel / 8 * 1 => float legAccelMax; 0 => float omega; // angular velocity gAccel * 3 * 1 => float swingFriction; //kneeAngle.last() => float prevKneeAngle; fun float calcInnerProduct(vec3 a, vec3 b) { return a.x*b.x + a.y*b.y + a.z*b.z; } fun float calcMag(vec3 in) { return Math.sqrt(Math.pow(in.x,2) + Math.pow(in.y,2) + Math.pow(in.z,2)); } fun void assumeFlyingPosition() { you.rot() + @(pi/2,0,0) => vec3 finalBodyRot; legs.rot() + @(pi/2,0,0) => vec3 finalLegsRot; kneeAngleEnv.keyOn(); .65::second => dur d; (d / physT)$int => int N; while (true) { physT => now; you.rot() => vec3 bodyRot; legs.rot() => vec3 legRot; (finalBodyRot - bodyRot)*(physT/d) => vec3 bodyDRot; (finalLegsRot - legRot)*(physT/d) => vec3 legsDRot; you.rotate(bodyDRot); legs.rotate(legsDRot); if (you.rot() == bodyRot && legs.rot() == legRot) { you.rot(finalBodyRot); legs.rot(finalLegsRot); break; } } //<<<"ya">>>; } fun void assumeSeatedPosition() { you.rot() + @(pi/2,0,0) => vec3 finalBodyRot; legs.rot() - @(pi/2,0,0) => vec3 finalLegsRot; kneeAngleEnv.keyOff(); .65::second => dur d; (d / physT)$int => int N; while (true) { physT => now; you.rot() => vec3 bodyRot; legs.rot() => vec3 legRot; (finalBodyRot - bodyRot)*(physT/d) => vec3 bodyDRot; (finalLegsRot - legRot)*(physT/d) => vec3 legsDRot; //you.rotate(bodyDRot); legs.rotate(legsDRot); if (you.rot() == bodyRot && legs.rot() == legRot) { you.rot(finalBodyRot); legs.rot(finalLegsRot); break; } } //<<<"ya">>>; } class Follower extends Chugen { Math.exp(-1*samp/.5::second) => float a1; Math.exp(-1*samp/1.5::second) => float a2; samp/4::second => float step; float cur; fun float tick (float in) { if (in < cur) { //(in - cur) * (1 - a1) +=> cur; Math.sgn(in - cur) * Math.min(Math.fabs(in - cur), step) +=> cur; } else { Math.sgn(in - cur) * Math.min(Math.fabs(in - cur), step) +=> cur; //(in - cur) * (1 - a2) +=> cur; } return cur; } } Step shoulderRotTarget => Follower shoulderAngle => blackhole; shoulderRotTarget.next(0); class FirstDiff extends Chugen { float prev; fun float tick (float in) { in - prev => float out; in => prev; return out; } } shoulderAngle => FirstDiff shoulderDiff => blackhole; float airVel; second/samp * (physT/second) * 30 => float wingA; 0 => float wingDrag; (physT/second)*80 => float airResistance; fun float calcShoulderRotOffset() { return shoulderAngle.last(); } //.8 => float angleSpurtRat; fun vec3 calcSpurtVec() { //return angleSpurtRat * (-swingArmAngle/(pi) + 0*(pi/2 - shoulderAngle.last()) / 2) * @(0,0,1); //<<>>; return airVel * @(0,0,1); } float movementGain; GGen flyingDolly; GGen cameraFlyingDolly; //vec3 initialSpurtVec; vec3 pathStartPoint; vec3 pathAscentVec; vec3 pathAscentEndPoint; 29 => float pathAscentEndProgress; pathAscentEndProgress + 100 => float pathLoopEndProgress; fun void calcPathPoints() { flyingDolly.posWorld() => pathStartPoint; -1 * flyingDolly.forward() => pathAscentVec;pathStartPoint + pathAscentEndProgress * pathAscentVec => pathAscentEndPoint; } fun float calcNorm(vec3 in) { return Math.sqrt(Math.pow(in.x,2) + Math.pow(in.y,2) + Math.pow(in.z,2)); } fun vec3 normalize(vec3 in) { return in / calcNorm(in); } false => int ascentDone; false => int pathDone; fun vec3 pathFn(float p) { if (p < pathAscentEndProgress) { return pathStartPoint + p * pathAscentVec; } else if (p < pathLoopEndProgress) { if (!ascentDone) { //0 => swingArmAngle; //0 => omega; true => ascentDone; } (p - pathAscentEndProgress) / (pathLoopEndProgress - pathAscentEndProgress) * 2 * pi => float theta; return pathAscentEndPoint + 40 * @(Math.sin(theta),0,Math.cos(theta)-1); } else if (p < pathLoopEndProgress + pathAscentEndProgress) { return pathAscentEndPoint - (p - pathLoopEndProgress) * pathAscentVec; } else { if (!pathDone) { you --> swingArm; assumeSeatedPosition(); GROUND_MODE => mode; true => pathDone; } return pathStartPoint; } } fun void startFlying() { you.pos() => vec3 youPos; you.rot() => vec3 youRot; you --< swingArm; you --> flyingDolly; you.pos(youPos); //-1 * calcSpurtVec() => initialSpurtVec; you.rot(youRot); FLY_MODE => mode; omega * topBar.posY() => airVel; flyingDolly --> sceneGen; flyingDolly.pos(swingArm.posWorld()); flyingDolly.rot(swingArm.rot() + swing.rot()); calcPathPoints(); camera.posWorld() => vec3 camPos; camera.lookAtDir() => vec3 camFow; cameraFlyingDolly.posWorld(flyingDolly.posWorld()); camera --> cameraFlyingDolly; camera.posWorld(camPos); camera.lookAt(camera.posWorld() + camFow); spork ~ assumeFlyingPosition(); youSong.setNPitches(2); movementGain => airVel; } .3::second => dur flyFollowTau; float prevCameraTranslateMag; float pathProgress; fun void runPhysics() { while (true) { //-legAccelMax*Math.cos(kneeAngle.last()*pi/2) * (kneeAngle.last() - prevKneeAngle) / (physT/second) => float legAccel; //kneeAngle.last() => prevKneeAngle; mode == GROUND_MODE && mouseDown ? -legAccelMax : 0. => float legAccel; if (mode == GROUND_MODE) { -Math.sin(swingArmAngle) * gAccel + legAccel - swingFriction * omega => float angularAccel; angularAccel * (physT/second) +=> omega; omega +=> swingArmAngle; } //<<>>; //<<>>; -shoulderDiff.last()*(shoulderDiff.last() < 0 ? wingA : wingDrag) - airResistance * airVel => float airAccel; //-shoulderDiff.last()*wingA - airResistance * airVel => float airAccel; airAccel * (physT/second) +=> airVel; //airVel +=> airPos; // TODO: make decision on backswing and accelerate? if (mode == GROUND_MODE && swingArmAngle < -pi/2 * 3/4 && omega < -.0098) { startFlying(); //<<<"yo">>>; } if (mode == FLY_MODE) { //<<>>; airVel +=> pathProgress; flyingDolly.posWorld(pathFn(pathProgress)); (flyingDolly.posWorld() - cameraFlyingDolly.posWorld()) * (physT/flyFollowTau) => vec3 cameraTranslateVec; calcInnerProduct(cameraTranslateVec, -1*flyingDolly.forward()) => float cameraTranslateMag; (cameraTranslateMag - prevCameraTranslateMag) / (physT/flyFollowTau) => movementGain; cameraTranslateMag => prevCameraTranslateMag; cameraFlyingDolly.translate(cameraTranslateVec); //spurtingDolly.pos(initialSpurtVec + calcSpurtVec()); mouseDown ? -pi/2/8 : pi/2/5 => shoulderRotTarget.next; } else if (mode == GROUND_MODE) { -topBar.posY() * omega => movementGain; } //<<>>; //<<>>; physT => now; } } spork ~ runPhysics(); .12 => float shouldRotRat; fun void setGraphicsParams() { knees.rot(@(pi/2*(1-kneeAngle.last()),0,0)); // TODO: body lean swingArm.rot(@(swingArmAngle,0,0)); //if (mode == FLY_MODE) { // TODO: initial armAngle to make smooth shoulderRot + calcShoulderRotOffset() => float shoulRot; -shoulRot => lShoulder.rotZ; shoulRot => rShoulder.rotZ; //} } fun void setAudioParams() { youSong.setG(movementGain); } //you.rot() => vec3 target; while (true) { setGraphicsParams(); setAudioParams(); GG.nextFrame() => now; //<<>>; //target + 1 * 2 * Math.pi * (2 * @(0,Math.randomf(),0) - @(0,1,0)) => target; //you.rot(you.rot() + .0001 * (target - you.rot())); }